package cn.linjianhui.api.client;

import cn.linjianhui.api.client.annotation.Service;
import cn.linjianhui.api.client.config.RpcClientConfig;
import cn.linjianhui.api.client.exception.RpcClientException;
import cn.linjianhui.api.client.invoker.HttpRequestParam;
import cn.linjianhui.api.client.invoker.RPCInvoker;
import cn.linjianhui.api.client.invoker.impl.HttpClientTokenAuthenticator;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;

public class RemoteServiceProxy implements InvocationHandler {

    private HttpClientTokenAuthenticator authenticator;

    private Class<?> proxyType;

    private RpcClientConfig config;

    RemoteServiceProxy(HttpClientTokenAuthenticator authenticator, Class<?> proxyType, RpcClientConfig config) {
        this.proxyType = proxyType;
        this.authenticator = authenticator;
        this.config = config;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Assert.notNull(proxyType, "Proxy object type cannot be empty");
        Service annotation = proxyType.getAnnotation(Service.class);
        if(ObjectUtils.isEmpty(annotation)) {
            throw new RpcClientException("Proxy class is not service annotated");
        }

        switch (method.getName()) {
            case "toString":
                return proxyType.getName();
            case "getClass":
                return proxyType;
            case "equals":
                return false;
            case "hashCode":
                return 0;
        }

        String baseAddress = this.config.getBaseAddress();
        String serviceKey = annotation.value();
        String serviceUri = String.format("/Service/%s/%s", serviceKey, method.getName());
        String requestUrl = String.format("%s%s", baseAddress, serviceUri);
        this.authenticator.setRequestParam(new HttpRequestParam(requestUrl));

        // 方法参数名字
        String[] parameters = new String[method.getParameterCount()];
        for(int index = 0; index < method.getParameterCount(); index ++) {
            parameters[index] = method.getParameters()[index].getName();
        }

        // 获取返回参数的泛型类型
        Class<?> genericType = null;
        if(method.getGenericReturnType() instanceof ParameterizedType) {
            ParameterizedType pt = (ParameterizedType) method.getGenericReturnType();
            if (pt.getActualTypeArguments().length > 0) {
                genericType = (Class<?>) pt.getActualTypeArguments()[0];
            }
        }

        RPCInvoker rpcInvoker = RPCInvokerFactory.GetInvoker(serviceUri, baseAddress, parameters,
                method.getReturnType(), genericType, this.authenticator, DataFormat.JSON);

        return rpcInvoker.doMethodInvoker(args);
    }
}
